﻿#region << 文件说明 >>

/*----------------------------------------------------------------
 * 命名空间：Vampirewal.Core.ScheduledTask
 * 唯一标识：2b513eae-cc96-421d-bae9-41032667e9f5
 * 文件名：VampirewalCoreScheduledTaskService
 *
 * 创建者：杨程
 * 电子邮箱：235160615@qq.com
 * 创建时间：2024/8/28 11:17:25
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion << 文件说明 >>

using Cronos;
using Microsoft.Extensions.DependencyInjection;
using System.Text;

namespace Vampirewal.Core.ScheduledTask;

/// <summary>
/// 定时任务服务
/// </summary>
internal class VampirewalCoreScheduledTaskService : IVampirewalCoreScheduledTaskService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ScheduledTaskOption Option;

    public VampirewalCoreScheduledTaskService(IServiceProvider serviceProvider, ScheduledTaskOption option)
    {
        _serviceProvider = serviceProvider;
        Option = option;
        IsRunning = false;
    }

    private bool IsRunning { get; set; }

    public void Running()
    {
        if (IsRunning) return;

        foreach (var item in Option.JobInfos)
        {
            ExcuteJob(item);
        }

        IsRunning = true;
    }

    private void ExcuteJob(JobInfo jobInfo)
    {
        var cts = new CancellationTokenSource();

        Task.Run(async () =>
        {
            var scope = _serviceProvider.CreateScope();

            var job = (IJob)scope.ServiceProvider.GetRequiredService(jobInfo.JobType);

            jobInfo.Context = new JobContext()
            {
                JobName = jobInfo.JobOption.JobName,
                JobDescription = jobInfo.JobOption.JobDescription,
            };

            int retryCount = 0;

            while (job != null && jobInfo.State == JobState.Running)
            {
                try
                {
                    var expression = CronExpression.Parse(jobInfo.JobOption.CronExpression, CronFormat.IncludeSeconds);

                    var nextOccurrence = expression.GetNextOccurrence(DateTime.UtcNow);

                    if (nextOccurrence.HasValue)
                    {
                        var delay = nextOccurrence.Value - DateTime.UtcNow;

                        await Task.Delay(delay);

                        await job.ExcuteAsync(jobInfo.Context);

                        jobInfo.Context.ExcuteCount++;

                        jobInfo.State = JobState.Running;

                    }
                }
                catch (CronFormatException formatException)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine($"-----<{DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")}>-----");
                    sb.AppendLine($"Job:{jobInfo.JobOption.JobName}");
                    sb.AppendLine($"Cron文本需要包含秒数，总共需要7节！");
                    sb.AppendLine($"当前Cron：{jobInfo.JobOption.CronExpression}");
                    sb.AppendLine($"该任务已停止执行！");
                    sb.AppendLine($"----------");

                    Exception exception = new Exception(sb.ToString());
                    jobInfo.Context.JobException = exception;
                    await job.OnExcuteErrorAsync(jobInfo.Context, exception);
                    jobInfo.State = JobState.Stop;
                    cts.Cancel();
                }
                catch (Exception ex)
                {
                    jobInfo.Context.JobException = ex;
                    await job.OnExcuteErrorAsync(jobInfo.Context, ex);
                    if (jobInfo.JobOption.RetryCount > 0 && jobInfo.JobOption.RetryCount >= retryCount)
                    {
                        retryCount++;
                    }
                    else
                    {
                        jobInfo.State = JobState.Stop;
                        cts.Cancel();
                    }
                }
            }
        }, cts.Token);
    }

}
